﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using XNOgre.Core;

namespace XNOgre.Graphics
{
    public class RenderSystem
    {
        private BasicEffect basicEffect;
        private EnvironmentMapEffect envMapEffect;
        private Effect _effect;

        private IEnumerable<Light> lights;
        private Pass pass;

        private int vpWidth, vpHeight;
        private Camera camera;

        internal RenderSystem(GraphicsDevice device)
        {
            GraphicsDevice = device;

            basicEffect = new BasicEffect(device);
            envMapEffect = new EnvironmentMapEffect(device);
        }

        public void Render(RenderOperation rop)
        {
            GraphicsDevice.SetVertexBuffer(rop.VertexData);
            if (rop.UseIndices)
            {
                GraphicsDevice.Indices = rop.IndexData;
            }

            _effect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.SamplerStates[0] = pass.TextureUnit.Sampler;
            GraphicsDevice.Textures[0] = pass.TextureUnit.Texture;

            switch (rop.OperationType)
            {
                case PrimitiveType.LineList:
                    if (rop.UseIndices)
                    {
                        GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, rop.VertexStartIndex, 0, rop.VertexCount, rop.IndexStartIndex, rop.PrimCount);
                    }
                    else
                    {
                        GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, rop.VertexStartIndex, rop.PrimCount);
                    }
                    break;
                case PrimitiveType.LineStrip:
                    if (rop.UseIndices)
                    {
                        GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineStrip,rop.VertexStartIndex, 0, rop.VertexCount, rop.VertexStartIndex, rop.PrimCount);
                    }
                    else
                    {
                        GraphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, rop.VertexStartIndex, rop.PrimCount);
                    }
                    break;
                case PrimitiveType.TriangleStrip:
                    if (rop.UseIndices)
                    {
                        GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, rop.VertexStartIndex, 0, rop.VertexCount, rop.VertexStartIndex, rop.PrimCount);
                    }
                    else
                    {
                        GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, rop.VertexStartIndex, rop.PrimCount);
                    }
                    break;
                case PrimitiveType.TriangleList:
                default:
                    if (rop.UseIndices)
                    {
                        GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, rop.VertexStartIndex, 0, rop.VertexCount, rop.IndexStartIndex, rop.PrimCount);
                    }
                    else
                    {
                        GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, rop.VertexStartIndex, rop.PrimCount);
                    }

                    break;
            }

            ResetEffect();
        }

        public GraphicsDevice GraphicsDevice { get; set; }

        public Color AmbientLight
        {
            get;
            set;
        }
        public IEffectFog Fog { get; set; }

        public float ShadowDirLightExtrusionDistance
        {
            get;
            set;
        }
        public Matrix World
        {
            get;
            set;
        }
        public Matrix View
        {
            get;
            set;
        }
        public Matrix Projection
        {
            get;
            set;
        }


        internal void SetLights(IEnumerable<Light> lights)
        {
            this.lights = lights;
        }
        internal void SetViewportSettings(ViewportTarget vp)
        {
            this.vpWidth = vp.ActualWidth;
            this.vpHeight = vp.ActualHeight;
        }
        internal void SetCameraSettings(Camera cam)
        {
            this.camera = cam;
        }
        internal void SetPassSettings(Pass pass)
        {
            this.pass = pass;
            GraphicsDevice.BlendState = pass.BlendState;
            GraphicsDevice.DepthStencilState = pass.DepthStencil;
            GraphicsDevice.RasterizerState = pass.RasterState;

            SetShader(pass.Shader);
        }

        private void SetShader(Shader shader)
        {
            if (shader != null)
            {
                if (shader.Effect != null) //has custom effect
                {
                    foreach (var key in shader.Parameters.Keys)
                    {
                        ShaderParameter param = shader.Parameters[key];
                        object value = null;
                        if (param.IsEngineParam)
                        {
                            value = GetEngineValue((GpuProgramParameter)param.Value, param.AutoConstant, param.AutoArrayCount);
                        }
                        else
                        {
                            value = param.Value;
                        }
                        
                        SetEffectParameter(shader.Effect, param.ParamName, value);
                    }

                    _effect = shader.Effect;
                }
                else
                {
                    _effect = BestDefaultEffect;
                    UpdateEffect();
                }
            }
            else
            {
                _effect = BestDefaultEffect;
                UpdateEffect();
            }
        }
        private Effect BestDefaultEffect
        {
            get
            {
                if (pass.TextureUnit.EnvMap != null && pass.TextureUnit.EnvMap.Length > 0)
                {
                    return envMapEffect;
                }
                else
                {
                    return basicEffect;
                }
            }
        }
        /// <summary>
        /// Called when not using a custom effect.
        /// Attempts to set the most attributes for the Effect chosen as the best Default Effect.
        /// </summary>
        private void UpdateEffect()
        {
            if (_effect is EnvironmentMapEffect)
            {
                TextureCube cube = Root.Instance.Content.Load<TextureCube>(pass.TextureUnit.EnvMap);
                
                envMapEffect.Alpha = pass.Alpha;
                envMapEffect.FresnelFactor = 1.0f;
                envMapEffect.EnvironmentMap = cube;
                envMapEffect.EnvironmentMapAmount = pass.TextureUnit.EnvMapAmount;
                envMapEffect.EnvironmentMapSpecular = Vector3.Zero;
                envMapEffect.Texture = pass.TextureUnit.Texture;
                
                if (pass.Lighting)
                {
                    envMapEffect.EmissiveColor = pass.Emissive;
                    envMapEffect.DiffuseColor = pass.Diffuse;
                }
                else //fully lit
                {
                    envMapEffect.EmissiveColor = Color.Black.ToVector3();
                    envMapEffect.DiffuseColor = Color.White.ToVector3();
                    envMapEffect.AmbientLightColor = Color.White.ToVector3();
                }
            }
            else
            {
                
                basicEffect.Alpha = pass.Alpha;
                if (pass.TextureUnit.Texture != null)
                {
                    basicEffect.Texture = pass.TextureUnit.Texture;
                }
                basicEffect.TextureEnabled = (basicEffect.Texture != null);

                basicEffect.EmissiveColor = pass.Emissive;
                basicEffect.DiffuseColor = pass.Diffuse;
                basicEffect.SpecularColor = pass.Specular;
                basicEffect.SpecularPower = pass.SpecularPower;

                basicEffect.AmbientLightColor = AmbientLight.ToVector3();
            }
           
            #region Matrices
            IEffectMatrices matrices = (_effect as IEffectMatrices);
            if (matrices != null)
            {
                matrices.World = World;
                matrices.View = View;
                matrices.Projection = Projection;
            }
            #endregion

            #region Lighting
            IEffectLights lighting = (_effect as IEffectLights);
            if (lighting != null)
            {
                if (pass.Lighting)
                {
                    lighting.LightingEnabled = true;
                    int lightCounter = 0;
                    foreach (Light light in lights)
                    {
                        switch (lightCounter)
                        {
                            case 0:
                                lighting.DirectionalLight0.Enabled = true;
                                lighting.DirectionalLight0.DiffuseColor = light.Diffuse.ToVector3();
                                lighting.DirectionalLight0.Direction = light.Direction;
                                lighting.DirectionalLight0.SpecularColor = light.Specular.ToVector3();
                                break;
                            case 1:
                                lighting.DirectionalLight1.Enabled = true;
                                lighting.DirectionalLight1.DiffuseColor = light.Diffuse.ToVector3();
                                lighting.DirectionalLight1.Direction = light.Direction;
                                lighting.DirectionalLight1.SpecularColor = light.Specular.ToVector3();
                                break;
                            case 2:
                                lighting.DirectionalLight2.Enabled = true;
                                lighting.DirectionalLight2.DiffuseColor = light.Diffuse.ToVector3();
                                lighting.DirectionalLight2.Direction = light.Direction;
                                lighting.DirectionalLight2.SpecularColor = light.Specular.ToVector3();
                                break;
                        }

                        lightCounter++;
                        if (lightCounter >= 3)
                            break;
                    }

                    if (_effect is BasicEffect)
                    {
                        basicEffect.SpecularPower = pass.SpecularPower;
                    }
                    
                }
                else
                {
                    lighting.LightingEnabled = false;
                }
            }
            #endregion

            #region Fog
            IEffectFog effectFog = (_effect as IEffectFog);
            if (effectFog != null)
            {
                if (pass.FogOverride.FogEnabled == true)
                {
                    effectFog = pass.FogOverride;
                }
                else
                {
                    effectFog = Fog;
                }
            }
            #endregion

        }
        private void ResetEffect()
        {
            IEffectLights lighting = (_effect as IEffectLights);
            if(lighting != null)
            {
                lighting.AmbientLightColor = Vector3.One;
                lighting.DirectionalLight0.DiffuseColor = Vector3.Zero;
                lighting.DirectionalLight0.Direction = -Vector3.UnitZ;
                lighting.DirectionalLight0.Enabled = true;
                lighting.DirectionalLight0.SpecularColor = Vector3.Zero;

                lighting.DirectionalLight1.DiffuseColor = Vector3.Zero;
                lighting.DirectionalLight1.Direction = -Vector3.UnitZ;
                lighting.DirectionalLight1.Enabled = true;
                lighting.DirectionalLight1.SpecularColor = Vector3.Zero;

                lighting.DirectionalLight2.DiffuseColor = Vector3.Zero;
                lighting.DirectionalLight2.Direction = -Vector3.UnitZ;
                lighting.DirectionalLight2.Enabled = true;
                lighting.DirectionalLight2.SpecularColor = Vector3.Zero;

                lighting.LightingEnabled = true;
            }

            IEffectFog fogEffects = (_effect as IEffectFog);
            if (fogEffects != null)
            {
                fogEffects = new XNOgre.Scene.FogSettings();
            }
        }
        private object GetEngineValue(GpuProgramParameter programParam, int autoIndex = 0, int arrayCount = 0)
        {
            int lightCount = 0;
            if (lights != null)
            {
                lights.Count();
            }

            switch (programParam)
            {
                case GpuProgramParameter.WorldMatrix:
                    return World;

                case GpuProgramParameter.InverseWorldMatrix:
                    return Matrix.Invert(World);

                case GpuProgramParameter.TransposeWorldMatrix:
                    return Matrix.Transpose(World);

                case GpuProgramParameter.InverseTransposeWorldMatrix:
                    return Matrix.Invert(Matrix.Transpose(World));

                case GpuProgramParameter.ViewMatrix:
                    return View;

                case GpuProgramParameter.InverseViewMatrix:
                    return Matrix.Invert(View);

                case GpuProgramParameter.TransposeViewMatrix:
                    return Matrix.Transpose(View);

                case GpuProgramParameter.InverseTransposeViewMatrix:
                    return Matrix.Invert(Matrix.Transpose(View));

                case GpuProgramParameter.ProjectionMatrix:
                    return Projection;

                case GpuProgramParameter.InverseProjectionMatrix:
                    return Matrix.Invert(Projection);

                case GpuProgramParameter.TransposeProjectionMatrix:
                    return Matrix.Transpose(Projection);

                case GpuProgramParameter.InverseTransposeProjectionMatrix:
                    return Matrix.Invert(Matrix.Transpose(Projection));

                case GpuProgramParameter.ViewProjMatrix:
                    return View * Projection;

                case GpuProgramParameter.InverseViewProjMatrix:
                    return Matrix.Invert(View * Projection);

                case GpuProgramParameter.TransposeViewProjMatrix:
                    return Matrix.Transpose(View * Projection);

                case GpuProgramParameter.InverseTransposeViewProjMatrix:
                    return Matrix.Invert(Matrix.Transpose(View * Projection));

                case GpuProgramParameter.WorldViewMatrix:
                    return World * View;

                case GpuProgramParameter.InverseWorldViewMatrix:
                    return Matrix.Invert(World * View);

                case GpuProgramParameter.TransposeWorldViewMatrix:
                    return Matrix.Transpose(World * View);

                case GpuProgramParameter.InverseTransposeWorldViewMatrix:
                    return Matrix.Invert(Matrix.Transpose(World * View));

                case GpuProgramParameter.WorldViewProjMatrix:
                    return World * View * Projection;

                case GpuProgramParameter.InverseWorldViewProjMatrix:
                    return Matrix.Invert(World * View * Projection);

                case GpuProgramParameter.TransposeWorldViewProjMatrix:
                    return Matrix.Transpose(World * View * Projection);

                case GpuProgramParameter.InverseTransposeWorldViewProjMatrix:
                    return Matrix.Invert(Matrix.Transpose(World * View * Projection));

                case GpuProgramParameter.FogColor:
                    return pass.FogOverride.FogEnabled ? pass.FogOverride : this.Fog;

                case GpuProgramParameter.FogParams:
                    IEffectFog fog = this.pass.FogOverride.FogEnabled ? pass.FogOverride : this.Fog;
                    return new Vector3(fog.FogStart, fog.FogEnd, 1.0f / (fog.FogEnd - fog.FogStart));

                case GpuProgramParameter.SurfaceAmbientColor:
                    return pass.Ambient;

                case GpuProgramParameter.SuraceDiffuseColor:
                    return pass.Diffuse;

                case GpuProgramParameter.SurfaceSpecularColor:
                    return pass.Specular;

                case GpuProgramParameter.SurfaceEmissiveColor:
                    return pass.Emissive;

                case GpuProgramParameter.SurfaceShininess:
                    return pass.SpecularPower;

                case GpuProgramParameter.LightCount:
                    return lightCount;

                case GpuProgramParameter.AmbientLightColor:
                    return AmbientLight.ToVector3();

                case GpuProgramParameter.LightDiffuseColor:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.Diffuse.ToVector3();
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightSpecularColor:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.Specular.ToVector3();
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightAttenuation:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                new Vector4(light.AttenuationRange, light.AttenuationConstant, light.AttenuationLinear, light.AttenuationQuadric);
                            }
                            counter++;
                        }
                        return Vector4.Zero;
                    }
                case GpuProgramParameter.SpotLightParams:
                    break;
                case GpuProgramParameter.LightPosition:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.Position;
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightPositionObjectSpace:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                Matrix invertedWorld = Matrix.Invert(World);
                                return Vector3.Transform(light.Position, invertedWorld);
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightPositionViewSpace:
                    return Vector3.Transform(this.camera.DerivedPosition, View);

                case GpuProgramParameter.LightDirection:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.Direction;
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightDirectionObjectSpace:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                Matrix source = Matrix.Invert(Matrix.Invert(Matrix.Transpose(World)));
                                Vector3 vec3 = Vector3.Transform(light.Direction, source);
                                return vec3;
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightDirectionViewSpace:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                Matrix source = Matrix.Invert(Matrix.Transpose(View));
                                return Vector3.Transform(light.Direction, source);
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.LightDistanceObjectSpace:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                Matrix source = Matrix.Invert(World);
                                return Vector3.Transform(light.Position, source);
                            }
                        }
                        return 0.0f;
                    }
                case GpuProgramParameter.LightDiffuseColorArray:
                    {
                        List<Vector3> diffuses = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                diffuses.Add(light.Diffuse.ToVector3());
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            diffuses.Add(Vector3.Zero);
                        }

                        return diffuses;
                    }
                case GpuProgramParameter.LightSpecularColorArray:
                    {
                        List<Vector3> speculars = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                speculars.Add(light.Specular.ToVector3());
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            speculars.Add(Vector3.Zero);
                        }

                        return speculars;
                    }
                case GpuProgramParameter.LightAttenuationArray:
                    {
                        List<Vector4> attenuations = new List<Vector4>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Vector4 attenuation = new Vector4(light.AttenuationRange, light.AttenuationConstant, light.AttenuationLinear, light.AttenuationQuadric);
                                attenuations.Add(attenuation);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            attenuations.Add(Vector4.Zero);
                        }

                        return attenuations;
                    }
                case GpuProgramParameter.LightPositionArray:
                    {
                        List<Vector3> positions = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                positions.Add(light.Position);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            positions.Add(Vector3.Zero);
                        }

                        return positions;
                    }
                case GpuProgramParameter.LightPositionObjectSpaceArray:
                    {
                        List<Vector3> positions = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                //positions.Add(light.Diffuse.ToVector3());
                                Matrix source = Matrix.Invert(World);
                                positions.Add(Vector3.Transform(light.Position, source));
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            positions.Add(Vector3.Zero);
                        }

                        return positions;
                    }
                case GpuProgramParameter.LightPositionViewSpaceArray:
                    {
                        List<Vector3> positions = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Vector3 vec3 = Vector3.Transform(light.Position, View);
                                positions.Add(vec3);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            positions.Add(Vector3.Zero);
                        }

                        return positions;
                    }
                case GpuProgramParameter.LightDirectionArray:
                    {
                        List<Vector3> directions = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                directions.Add(light.Direction);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            directions.Add(Vector3.Zero);
                        }

                        return directions;
                    }
                case GpuProgramParameter.LightDirectionObjectSpaceArray:
                    {
                        List<Vector3> directions = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Matrix source = Matrix.Invert(Matrix.Invert(Matrix.Transpose(World)));
                                Vector3 vec3 = Vector3.Transform(light.Direction, source);
                                directions.Add(vec3);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            directions.Add(Vector3.Zero);
                        }

                        return directions;
                    }
                case GpuProgramParameter.LightDirectionViewSpaceArray:
                    {
                        List<Vector3> directions = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Matrix source = Matrix.Invert(Matrix.Transpose(View));
                                directions.Add(Vector3.Transform(light.Direction, source));
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            directions.Add(Vector3.Zero);
                        }

                        return directions;
                    }

                case GpuProgramParameter.LightDistanceObjectSpaceArray:
                    {
                        List<Vector3> distances = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Matrix source = Matrix.Invert(World);
                                distances.Add(Vector3.Transform(light.Position, source));
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            distances.Add(Vector3.Zero);
                        }

                        return distances;
                    }
                case GpuProgramParameter.DerivedAmbientLightColor:
                    return AmbientLight.ToVector3() * pass.Ambient;

                case GpuProgramParameter.DerivedSceneColor:
                    Vector3 derivedAmbient = AmbientLight.ToVector3() + pass.Emissive;
                    return derivedAmbient;

                case GpuProgramParameter.DerivedLightDiffuseColor:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.Diffuse.ToVector3() * pass.Diffuse;
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.DerivedLightSpecularColor:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.Specular.ToVector3() * pass.Specular;
                            }
                            counter++;
                        }
                        return Vector3.Zero;
                    }
                case GpuProgramParameter.DerivedLightDiffuseColorArray:
                    {
                        List<Vector3> diffuses = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Vector3 diffuse = light.Diffuse.ToVector3() * pass.Diffuse;
                                diffuses.Add(diffuse);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            diffuses.Add(Vector3.Zero);
                        }

                        return diffuses;
                    }
                case GpuProgramParameter.DerivedLightSpecularColorArray:
                    {
                        List<Vector3> speculars = new List<Vector3>();
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter < arrayCount || arrayCount == 0)
                            {
                                Vector3 specular = light.Specular.ToVector3() * pass.Specular;
                                speculars.Add(specular);
                            }
                        }
                        int remaining = arrayCount - counter;
                        if (remaining < 0)
                            remaining = 0;

                        for (int i = 0; i < remaining; i++)
                        {
                            speculars.Add(Vector3.Zero);
                        }

                        return speculars;
                    }
                case GpuProgramParameter.LightCastsShadows:
                    {
                        int counter = 0;
                        foreach (var light in lights)
                        {
                            if (counter == autoIndex)
                            {
                                return light.CastsShadows;
                            }
                            counter++;
                        }
                        return false;
                    }
                case GpuProgramParameter.CameraPosition:
                    return camera.DerivedPosition;

                case GpuProgramParameter.CameraPositionObjectSpace:
                    Matrix invWorld = Matrix.Invert(World);
                    return Vector3.Transform(camera.Position, invWorld);

                case GpuProgramParameter.ViewportWidth:
                    return vpWidth;

                case GpuProgramParameter.ViewportHeight:
                    return vpHeight;

                case GpuProgramParameter.InverseViewportWidth:
                    return 1.0f / vpWidth;

                case GpuProgramParameter.InverseViewportHeight:
                    return 1.0f / vpHeight;

                case GpuProgramParameter.ViewportSize:
                    return new Vector4(vpWidth, vpHeight, 1.0f / vpWidth, 1.0f / vpHeight);

                case GpuProgramParameter.FOV:
                    return camera.FieldOfView;

                case GpuProgramParameter.NearClipDistance:
                    return camera.NearClipDistance;

                case GpuProgramParameter.FarClipDistance:
                    return camera.FarClipDistance;
            }

            //should have returned by now
            throw new InvalidOperationException();
        }
        private void SetEffectParameter(Effect effect, string paramName, object value)
        {
            if (value.GetType() == typeof(bool))
            {
                effect.Parameters[paramName].SetValue((bool)value);
            }
            else if (value.GetType() == typeof(bool[]))
            {
                effect.Parameters[paramName].SetValue((bool[])value);
            }
            else if (value.GetType() == typeof(float))
            {
                effect.Parameters[paramName].SetValue((float)value);
            }
            else if (value.GetType() == typeof(float[]))
            {
                effect.Parameters[paramName].SetValue((float[])value);
            }
            else if (value.GetType() == typeof(int))
            {
                effect.Parameters[paramName].SetValue((int)value);
            }
            else if (value.GetType() == typeof(int[]))
            {
                effect.Parameters[paramName].SetValue((int[])value);
            }
            else if (value.GetType() == typeof(Matrix))
            {
                effect.Parameters[paramName].SetValue((Matrix)value);
            }
            else if (value.GetType() == typeof(Matrix[]))
            {
                effect.Parameters[paramName].SetValue((Matrix[])value);
            }
            else if (value.GetType() == typeof(Quaternion))
            {
                effect.Parameters[paramName].SetValue((Quaternion)value);
            }
            else if (value.GetType() == typeof(Quaternion[]))
            {
                effect.Parameters[paramName].SetValue((Quaternion[])value);
            }
            else if (value.GetType() == typeof(string))
            {
                effect.Parameters[paramName].SetValue((string)value);
            }
            else if (value.GetType() == typeof(Texture))
            {
                Texture texture = value as Texture;
                effect.Parameters[paramName].SetValue(texture);
            }
            else if (value.GetType() == typeof(Texture2D))
            {
                Texture2D texture = value as Texture2D;
                effect.Parameters[paramName].SetValue(texture);
            }
            else if (value.GetType() == typeof(Texture3D))
            {
                Texture3D texture = value as Texture3D;
                effect.Parameters[paramName].SetValue(texture);
            }
            else if (value.GetType() == typeof(RenderTarget2D))
            {
                RenderTarget2D texture = value as RenderTarget2D;
                effect.Parameters[paramName].SetValue(texture);
            }
            else if (value.GetType() == typeof(Vector2))
            {
                effect.Parameters[paramName].SetValue((Vector2)value);
            }
            else if (value.GetType() == typeof(Vector2[]))
            {
                effect.Parameters[paramName].SetValue((Vector2[])value);
            }
            else if (value.GetType() == typeof(Vector3))
            {
                effect.Parameters[paramName].SetValue((Vector3)value);
            }
            else if (value.GetType() == typeof(Vector3[]))
            {
                effect.Parameters[paramName].SetValue((Vector3[])value);
            }
            else if (value.GetType() == typeof(Vector4))
            {
                effect.Parameters[paramName].SetValue((Vector4)value);
            }
            else if (value.GetType() == typeof(Vector4[]))
            {
                effect.Parameters[paramName].SetValue((Vector4[])value);
            }
            else if (value.GetType() == typeof(Color))
            {
                Color color = (Color)value;
                effect.Parameters[paramName].SetValue(color.ToVector3());
            }
            else
            {
                throw new XNOgreException("The given object was not a valid effect parameter type.");
            }
        }
    }
}
